#基于tinydb的web表单， 可以增加，删除，修改条目， 也可以查询
#开放web接口，外部程序可以通过web api调用
#运行方法 python3 -m uvicorn 本代码名:app --host '0.0.0.0' --port 8001 --reload
from fastapi import FastAPI

import json

from tinydb import TinyDB, where
from tinydb import Query
from starlette.responses import HTMLResponse
from starlette.responses import RedirectResponse
from starlette.staticfiles import StaticFiles


import ast
import copy
def convertExpr2Expression(Expr):
        Expr.lineno = 0
        Expr.col_offset = 0
        result = ast.Expression(Expr.value, lineno=0, col_offset = 0)

        return result
def exec_with_return(code):
    code_ast = ast.parse(code)

    init_ast = copy.deepcopy(code_ast)
    init_ast.body = code_ast.body[:-1]

    last_ast = copy.deepcopy(code_ast)
    last_ast.body = code_ast.body[-1:]

    exec(compile(init_ast, "<ast>", "exec"), globals())
    if type(last_ast.body[0]) == ast.Expr:
        return eval(compile(convertExpr2Expression(last_ast.body[0]), "<ast>", "eval"),globals())
    else:
        exec(compile(last_ast, "<ast>", "exec"),globals())
        


def addnew(file,q):
    
    db = TinyDB(file)
    db.insert(q)



app = FastAPI()

app.mount('/static', StaticFiles(directory='../static'), name='static')
app.mount('/layui', StaticFiles(directory='../static/layui'), name='layui') 






html1='''
<!DOCTYPE html>
<html>
<head>
  <meta charset="utf-8">
  <title>table模块快速使用</title>
  <link rel="stylesheet" href="/layui/css/layui.css" media="all">
          <script type="text/javascript">
    //直接弹出，无取消按钮    
function huoqu(){
             //alert(2);
        var luckElements = document.getElementsByName("fname");
         
        /*
            第二步 遍历节点数组
        */
    let pStr = '';
        for(var i=0;i<luckElements.length;i++){
            //获取元素的value值
            //alert(luckElements[i].value);
            
            pStr = pStr + luckElements[i].value+"|";
            //alert(pStr);
            //获取元素的type值
            //alert(luckElements[i].type);
            //为每一个元素的onclick属性赋值即为文本框增加点击事件
            
                
                //let x=x+"|"+this.value;
                //alert(x);
            
        }
    
var name = document.getElementById("name");
name.value = pStr;

        }
</script>
</head>
<body>
<center><h1>轻量级web表单Demo</h1></center>
<center><form action="" method="get">
    <div class="layui-form-item">
    <div class="layui-inline" >
    <label class="layui-form-label"></label>
    <div class="layui-form-item">
     <input type="text" name="search"  placeholder='db.search(User.部门=="IT")' autocomplete="on" class="layui-input-inline" style="text-align: center;background-color: #f3fdff;width: 520px;height: 40px;">
     <button class="layui-btn layui-btn-normal" lay-submit lay-filter="formDemo">搜索</button>
     <a href="https://tinydb.readthedocs.io/en/stable/usage.html#query-modifiers" class="layui-btn" target="_blank">高级搜索</a><a href="/help" class="layui-btn" target="_blank">web接口</a>
     </center>
    </div>  
    </div>
    
  </div>

</form>
</center>
<form  name="input" action="" method="get"> 


<!--此处设置列宽-->
  <table class="layui-table">
    <colgroup>
    <col width="10">
    <col width="80">
    <col width="80">
    <col width="80">
    <col width="120">
    
  </colgroup>
  
  <thead>
    <tr>
    
%s
    </tr> 
  </thead>
%s
  </table>
<input hidden type="text" id="name" name="fnames"/>






</form>
分页显示<a href="?page=1">1 </a><a href="?page=2">2 </a><a href="?page=3">3 </a><a href="?page=4">4 </a><a href="?page=5">5 </a><a href="?page=6">6 </a><a href="?page=7">7 </a><a href="?page=8">8 </a><a href="?page=9">9 </a><a href="?page=10">10 </a><a href="?page=11">11 </a><a href="?page=12">12 </a><a href="?page=13">13 </a><a href="?page=14">14 </a>

</body>
</html>
'''


     
    
@app.get("/tables/{item_id}")
async def read_item2(item_id,insert:str=None,page:int=1,limit:int=10,id:int=None,action:str=None,fnames:str=None,search:str=None):
    print('fnames:',fnames)
    print("ID:",id)
    print("action:",action)
    print("search:",search)
    print("insert",insert)
    global db,User
    db=TinyDB(item_id+'.json')
    User = Query()
    #新增api
    if insert:
        print(insert)
        a=json.loads(insert)
        print(a)
        #db=TinyDB(item_id+'.json')
        db.insert(a)
        return RedirectResponse('./'+item_id)
        
    #删除ID对应记录
    if action =='remove':
        db.remove(doc_ids=[id])
        #db=TinyDB(item_id+'.json')
        return RedirectResponse('./'+item_id)
    dict0={}
    dict0['code']=0
    #if search:
        #dict0['data']=exec_with_return(search)
    #    dict0['data']=db.all()[::-1][(page-1)*limit:(page-1)*limit+limit]
    #    print(dict0['data'],'######################')
        #id='abc'
    #    print('search', dict0['data'])
    #    print("%%%%%%%%%%%--------%%%%%%%%%%%%%%%%%%")
    #列出所有
    if id is None and search is None:
        dict0['data']=db.all()[::-1][(page-1)*limit:(page-1)*limit+limit]
        print('id',id,dict0['data'])
    
        print("%%%%%%%%%%%stop here%%%%%%%%%%%%%%%%%%")
    #搜索条件
    elif search:
        dict0['data']=exec_with_return(search)
    #列出指定ID
    else:
        dict0['data']=[]
        dict0['data'].append(db.get(doc_id=id))
        #list(db.get(doc_id=3))
        #print('***********',db.get(doc_id=3),dict0['data'])
    cols=list(db.all()[-1].keys())
    cols.insert(0,'id')
    str0=''
    #str0='<td><label><input name="Created_Date" type="radio" value="hello"></label></td>'
    for col in cols:
        str0=str0+'<th>'+col+'</th>'
    str2=''
    #str2='<td><label><input name="Created_Date" type="radio" value="hello"></label></td>'
    for item in dict0['data']:
        list1=list(item.values())
        #list1.insert(0,item.doc_id)
        str1='<tr><td><label><input name="id" type="radio" value=%s checked="checked"</label></td>'%(item.doc_id)
        if action=='update':
            for x in list1:
                #<input type="text" name="fname" value="George" >
                str1=str1+'<td>'+'<input type="text" name="fname" value="%s" class="layui-input">'%(str(x))+'</td>'
            str2=str2+str1+'</tr>'
            #pass
        else:
            for x in list1:
                str1=str1+'<td>'+str(x)+'</td>'
            str2=str2+str1+'</tr>'
    str3='<tr><td><button  class="layui-btn layui-btn-xs layui-btn-normal" name="action" type="submit" value="new" onclick="huoqu();">+</button></td>'
    for i in range(len(cols)-1):
        str3=str3+'<td><input type="text" name="fname" class="layui-input"></td>'
    str3=str3+'</td>'
    #str2=str2+str3
    #显示全部的记录
    if fnames is None:
        print("*******1")
        str2=str2+str3+'</table><center><br><button  class="layui-btn layui-btn-warm" name="action" type="submit" value="update">修改</button><button  class="layui-btn layui-btn-danger" name="action" type="submit" value="remove" >删除</button><a href="%s" class="layui-btn">返回</a></center>'%(item_id)
        return HTMLResponse(content=html1 % (str0,str2))
    #显示要更新的单个条目，并可修改
    elif fnames == "":
        print("*******2")
        str2=str2+'</table><br><button  class="layui-btn layui-btn-normal" name="action" type="submit" value="update2" onclick="huoqu();">修改确认<button><br>'
        print(str2)
        return HTMLResponse(content=html1 % (str0,str2))
    #新增条目
    elif action =='new':
    #else:
        print("*******3")
        print('fnames here:',fnames)
        print('cols:',cols)
        values=fnames.split('|')
        dictx={}
        for i in range(1,len(cols)):
            dictx[cols[i]]=values[i-1]
        print(dictx)
        jsonnew=str(dictx).replace('\'','\"')
        print(jsonnew)
        return RedirectResponse('./'+item_id+'?insert='+jsonnew)
    #elif action=="update1":
    #    return RedirectResponse('./'+item_id)
    else:
        print("*******4")
        print('fnames here:',fnames)
        print('cols:',cols)
        values=fnames.split('|')
        dictx={}
        for i in range(1,len(cols)):
            dictx[cols[i]]=values[i-1]
        print(dictx)
        db.update(dictx, doc_ids=[id])
        return RedirectResponse('./'+item_id)
        
        

        #response = requests.get('json/abc')
    #str2=str2+'<tr><td><input type="text" name="fname" ></td><td><input type="text" name="fname1" ></td><td><input type="text" name="fname2" ></td><td><input type="text" name="fname3" ></td></tr>'
    print("*******5")
    return HTMLResponse(content=html1 % (str0,str2))    


@app.get("/search/{item_id}")
async def read_item3(item_id,insert:str=None,page:int=1,limit:int=10,id:int=None,action:str=None,fnames:str=None,field:str=None,value:str=None,search:str=None):
    print('fnames:',fnames)
    print("ID:",id)
    print("action:",action)
    print("search:",search)
    print("field:",field)
    print("value:",value)
    global db,User
    db=TinyDB(item_id+'.json')
    User = Query()
    #x=exec_with_return("db.search(where('部门') == 'c1')")
    result=exec_with_return(search)
    #print(x,type(x))
    #for item in x:
    #    print(item)
    #print(exec ("db.search(where('部门') == 'c1')"))
    #print("*********88******")
    #result=db.search(User.部门 == 'c1')
    #search="User.部门 == 'c1'"
    #result=db.search(where('部门') == 'c1')
    #result=db.search(where(field) == value)
    return result

help0='''
<html>
<body>
<h2>简介</h2>
本程序使用tinydb为后台数据库， 容量限额约为100M， 10万条左右的数据， 再大json会卡死， 适用于小型程序的数据库， 文件型数据库， 几乎无需安装
命令也基本都是tinydb的命令， 做了网络接口而已。
<h3>如何创建新表单？</h3>
举例 http://你的IP:端口/tables/这里是你要建的表单名称?insert={"机器名":"Nan1006","使用人":"八王爷","部门":"IT","日期":"2020-06-22"}
传入一个json文件后, 表格自动生成， 注意 传入时json格式一定是双引号， 单引号会报错， 另外字段内不得包含特殊字符。
同理后期更改列明也是同样的方法。
<h3>如何传入数据？</h3>
答案和上面一模一样，持续将 json数据注入web接口即可
<h3>如何通过web接口查询数据？</h3>
http://你的IP:端口/search/你的表名?search=db.all()
search=后面的语句就是查询语句， 
db.all()   是查询所有记录
db.search(User.name == 'John')  查询name列名字为John的记录
db.search(User['name'] == 'John')  也可以写成这样， python的兼容性更好
db.search(~ (User.name == 'John'))  取非记录
db.search((User.name == 'John') & (User.age <= 30))   逻辑且
db.search((User.name == 'John') | (User.name == 'Bob'))  逻辑或

其他的查询命令详见https://tinydb.readthedocs.io/en/stable/usage.html#query-modifiers
<h3>如何导出所有数据</h3>
更上一问题一样，db.all()就能查到所有记录
<h3>如何修改记录?</h3>
事实上search/你的表名?search= 后面跟的所有db命令都会被执行， 所以看看官方文档， 把命令加到这里即可
<h3>如何删除记录</h3>
答案同上
</body>
</html>
'''

#帮助
@app.get("/help/")
async def help():
    return HTMLResponse(content=help0)